var CodeMirror = require('codemirror');
var events = require('./events');
var protocols = require('./protocols');
var forwardRules = protocols.getForwardRules();
var pluginRules = protocols.getPluginRules();

events.on('updatePlugins', function() {
  forwardRules = protocols.getForwardRules();
  pluginRules = protocols.getPluginRules();
});


CodeMirror.defineMode('rules', function() {
  function isIP(str) {
    return /^(?:\/\/)?(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?:\:\d+)?$/.test(str);
  }
  function isHost(str) {
    return /^hosts?:\/\//.test(str);
  }
  function isHead(str) {
    return /^head:\/\//.test(str);
  }

  function isWeinre(str) {
    return /^weinre:\/\//.test(str);
  }

  function isReq(str) {
    return /^(?:req|hostname|reqHost|accept|etag|referer|auth|ua|forwardedFor|reqCookies|reqDelay|reqSpeed|reqCors|reqHeaders|method|reqType|reqCharset|reqBody|reqPrepend|reqAppend|reqReplace|reqWrite|reqWriteRaw):\/\//.test(str);
  }

  function isRes(str) {
    return /^(?:resScript|responseFor|res|resCookies|resHeaders|statusCode|status|replaceStatus|redirect|resDelay|resSpeed|resCors|resType|resCharset|cache|attachment|download|location|resBody|resPrepend|resAppend|css(?:Append|Prepend|Body)?|html(?:Append|Prepend|Body)?|js(?:Append|Prepend|Body)?|resReplace|resWrite|resWriteRaw):\/\//.test(str);
  }

  function isUrl(str) {
    return /^(?:https?|wss?|tunnel):\/\//i.test(str);
  }

  function isRule(str) {
    return /^[\w\.-]+:\/\//i.test(str);
  }

  function notExistRule(str) {
    str = str.substring(0, str.indexOf(':'));
    return forwardRules.indexOf(str) == -1;
  }

  function notExistPlugin(str) {
    str = str.substring(0, str.indexOf(':'));
    return pluginRules.indexOf(str) == -1;
  }

  function isRegExp(str) {
    return /^\/[^/](.*)\/i?$/.test(str) || /^\$/.test(str);
  }

  function isParams(str) {
    return /^(?:urlParams|params|urlReplace):\/\//.test(str);
  }

  function isLog(str) {
    return /^log:\/\//.test(str);
  }

  function isFilter(str) {
    return /^filter:\/\//.test(str);
  }

  function isPlugin(str) {
    return (/^plugin:\/\//.test(str) || /^(?:plugin|whistle)\.[a-z\d_\-]+:\/\//.test(str)) && !notExistPlugin(str);
  }

  function isRulesFile(str) {
    return /^(?:rules?(?:File|Script)|reqScript):\/\//.test(str);
  }

  function isDisable(str) {
    return /^disable:\/\//.test(str);
  }

  function isIgnore(str) {
    return /^ignore:\/\//.test(str);
  }

  function isEnable(str) {
    return /^enable:\/\//.test(str);
  }

  function isDelete(str) {
    return /^delete:\/\//.test(str);
  }

  function isExports(str) {
    return /^exports?:\/\//.test(str);
  }

  function isExportsUrl(str) {
    return /^exports?Url:\/\//.test(str);
  }

  function isDispatch(str) {
    return /^dispatch:\/\//.test(str);
  }

  function isProxy(str) {
    return /^(?:proxy|http-proxy|http2https-proxy|https2http-proxy|internal-proxy):\/\//.test(str);
  }

  function isSocks(str) {
    return /^socks:\/\//.test(str);
  }

  function isPac(str) {
    return /^pac:\/\//.test(str);
  }

  function isLocalPath(str) {
    return /^[a-z]:(?:\\|\/(?!\/))/i.test(str) || /^\/[^/]/.test(str);
  }

  function isWildcard(str) {
    if (!/^(?:\$?(?:https?:|wss?:|tunnel:)?\/\/)?([^/]+)/.test(str)) {
      return false;
    }
    return RegExp.$1.indexOf('*') !== -1 || RegExp.$1.indexOf('~') !== -1;
  }

  function isRegUrl(url) {
    return /^\^/.test(url);
  }

  return {
    token: function(stream, state) {
      if (stream.eatSpace()) {
        return null;
      }

      var ch = stream.next();
      if (ch == '#') {
        stream.eatWhile(function(ch) {
          return true;
        });
        return 'comment';
      }

      var not = ch === '!';
      var str = not ? stream.next() : ch;
      var type = '';
      var pre;
      stream.eatWhile(function(ch) {
        if (/\s/.test(ch) || ch == '#') {
          return false;
        }
        str += ch;
        if (!type && ch == '/' && pre == '/') {
          if (isHost(str)) {
            type = 'number js-number';
          } else if (isHead(str)) {
            type = 'header js-head js-type';
          } else if (isWeinre(str)) {
            type = 'atom js-weinre js-type';
          } else if (isProxy(str)) {
            type = 'tag js-proxy js-type';
          } else if (isReq(str)) {
            type = 'variable-2 js-req js-type';
          } else if (isRes(str)) {
            type = 'positive js-res js-type';
          } else if (isParams(str)) {
            type = 'meta js-params js-type';
          } else if (isLog(str)) {
            type = 'atom js-log js-type';
          } else if (isPlugin(str)) {
            type = 'variable-2 js-plugin js-type';
          } else if (isFilter(str)) {
            type = 'negative js-filter js-type';
          } else if (isIgnore(str)) {
            type = 'negative js-ignore js-type';
          } else if (isEnable(str)) {
            type = 'atom js-enable js-type';
          } else if (isDisable(str)) {
            type = 'negative js-disable js-type';
          } else if (isDelete(str)) {
            type = 'negative js-delete js-type';
          } else if (isExports(str)) {
            type = 'atom js-exports js-type';
          } else if (isExportsUrl(str)) {
            type = 'atom js-exportsUrl js-type';
          } else if (isDispatch(str)) {
            type = 'variable-2 js-dispatch js-type';
          } else if (isProxy(str)) {
            type = 'variable-2 js-proxy js-type';
          } else if (isSocks(str)) {
            type = 'variable-2 js-socks js-type';
          } else if (isPac(str)) {
            type = 'variable-2 js-pac js-type';
          } else if (isRulesFile(str)) {
            type = 'variable-2 js-rulesFile js-type';
          } else if (isUrl(str)) {
            type = 'string-2 js-url js-type';
          } else if (isWildcard(str)) {
            type = 'attribute js-attribute';
          } else if (isRule(str)) {
            type = 'builtin js-rule js-type' + (notExistRule(str) ? ' error-rule' : '');
          }
        }
        pre = ch;
        return true;
      });
      if (!type) {
        if (isRegExp(str) || isRegUrl(str)) {
          return 'attribute js-attribute';
        }
        if (/^@/.test(str)) {
          type = 'atom js-at js-type';
        } else if (isWildcard(str)) {
          type = 'attribute js-attribute';
        } else if (isIP(str)) {
          type = 'number js-number';
        } else if (/^\{.*\}$/.test(str) || /^<.*>$/.test(str) || /^\(.*\)$/.test(str)) {
          type = 'builtin js-rule js-type';
        } else if (isLocalPath(str)) {
          type = 'builtin js-rule js-type';
        }
      }
      return not ? type + ' error-rule' : type;
    }
  };
});
